from re import A
import streamlit as st
import os

import time
import jwt
import requests
import pandas as pd
import matplotlib.pyplot as plt

#api: 196b4f9be04346c7964bd306073f37a7.Szl43fu3WKUmkzTq
#运行: streamlit run glm_demo.py 
#funtion call

# 实际KEY，过期时间

def generate_token(apikey: str, exp_seconds: int):
    try:
        id, secret = apikey.split(".")
    except Exception as e:
        raise Exception("invalid apikey", e)

    payload = {
        "api_key": id,
        "exp": int(round(time.time() * 1000)) + exp_seconds * 1000,
        "timestamp": int(round(time.time() * 1000)),
    }
    return jwt.encode(
        payload,
        secret,
        algorithm="HS256",
        headers={"alg": "HS256", "sign_type": "SIGN"},
    )

def ask_glm(key, model, max_tokens, temperature, content):
    url = "https://open.bigmodel.cn/api/paas/v4/chat/completions"
    headers = {
      'Content-Type': 'application/json',
      'Authorization': generate_token(key, 1000)
    }

    data = {
        "model": model,
        "max_tokens": max_tokens,
        "temperature": temperature,
        "messages": content
    }

    response = requests.post(url, headers=headers, json=data)
    return response.json()

st.set_page_config(page_title="聊天机器人")

# 初始化的对话
if "messages" not in st.session_state.keys():
    st.session_state.messages = [{"role": "assistant", "content": "你好我是ChatGLM，有什么可以帮助你的？"}]

# function call
def handle_salary_query():
    # 获取用户输入的城市（从session_state中读取）
    city = st.session_state.city_query.strip() or None
    
    # 添加用户查询记录到对话历史
    user_query = f"查询薪资{city if city else '所有城市'}"
    st.session_state.messages.append({"role": "user", "content": user_query})
    
    # 在右侧聊天框显示用户消息
    with st.chat_message("user"):
        st.markdown(user_query)
    
    # 在右侧聊天框显示助手响应
    with st.chat_message("assistant"):
        st.markdown(f"已为您查询{'所有城市' if not city else city}的程序员平均薪资：")
        # 调用分析函数显示表格和图表
        show_salary_analysis(city)
        # 添加文本响应到对话历史
        st.session_state.messages.append({
            "role": "assistant", 
            "content": f"已展示{'所有城市' if not city else city}的程序员平均薪资数据（包含表格和图表）"
        })


# 将保留的历史对话展示在界面上
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.write(message["content"])

def clear_chat_history():
    st.session_state.messages = [{"role": "assistant", "content": "你好我是ChatGLM，有什么可以帮助你的？"}]

st.sidebar.button('清空聊天记录', on_click=clear_chat_history)

#左侧
with st.sidebar:
    st.title('通过API与大模型的对话')
    # st.write('支持的大模型包括的ChatGLM3和4')
    if 'API_TOKEN' in st.session_state and len(st.session_state['API_TOKEN']) > 1:
        st.success('API Token已经配置', icon='✅')
        key = st.session_state['API_TOKEN']
    else:
        key = ""

    key = st.text_input('输入Token:', type='password', value=key)

    st.session_state['API_TOKEN'] = key

    model = st.selectbox("选择模型", ["glm-3-turbo", "glm-4"])
    max_tokens = st.slider("max_tokens", 0, 2000, value=512)
    temperature = st.slider("temperature", 0.0, 2.0, value=0.8)
    

    #添加内容


# 添加岗位推荐功能
def query_job_data(position=None, city=None):
    """
    查询本地Skill.csv数据集中的岗位数据
    Args:
        position: 可选参数，指定岗位名称
        city: 可选参数，指定城市名称
    Returns:
        DataFrame: 包含岗位数据的DataFrame
    """
    try:
        df = pd.read_csv('Skill.csv')
        
        # 转换薪资范围为数值
        def convert_salary(salary_str):
            if pd.isna(salary_str):
                return None
            try:
                if '-' in salary_str:
                    low, high = salary_str.split('-')
                    low = float(low.replace('k', '')) * 1000
                    high = float(high.replace('k', '')) * 1000
                    return (low + high) / 2  # 返回平均值
                elif 'k' in salary_str:
                    return float(salary_str.replace('k', '')) * 1000
                else:
                    return float(salary_str)
            except:
                return None
                
        df['salary_numeric'] = df['salary'].apply(convert_salary)
        
        if position:
            df = df[df['positionName'].str.contains(position, case=False)]
        if city:
            df = df[df['city'] == city]
        return df[['positionName', 'companyFullName', 'city', 'salary', 'salary_numeric', 'workYear', 'education']]
    except Exception as e:
        st.error(f"读取数据失败: {e}")
        return None

def show_job_recommendations(position=None, city=None):
    """
    展示岗位推荐结果，包括表格和图表
    """
    df = query_job_data(position, city)
    if df is None or df.empty:
        st.warning("没有找到匹配的岗位数据")
        return
    
    # 显示数据表格
    st.dataframe(df)
    
    # 显示薪资分布图
    fig, ax = plt.subplots(figsize=(10, 6))
    df.groupby('positionName')['salary_numeric'].mean().plot.bar(ax=ax)
    ax.set_title('各岗位平均薪资')
    ax.set_ylabel('平均薪资')
    st.pyplot(fig)

#查询岗位推荐
def handle_job_query():
    # 获取用户输入的岗位和城市
    position = st.session_state.position_query.strip() or None
    city = st.session_state.city_job_query.strip() or None
    
    # 添加用户查询记录到对话历史
    query_text = f"查询岗位推荐: {position if position else '所有岗位'}{f' in {city}' if city else ''}"
    st.session_state.messages.append({"role": "user", "content": query_text})
    
    # 在右侧聊天框显示用户消息
    with st.chat_message("user"):
        st.markdown(query_text)
    
    # 在右侧聊天框显示助手响应
    with st.chat_message("assistant"):
        st.markdown(f"为您推荐{'所有岗位' if not position else position}{f'在{city}' if city else ''}的相关岗位：")
        # 调用分析函数显示表格和图表
        show_job_recommendations(position, city)
        # 添加文本响应到对话历史
        st.session_state.messages.append({
            "role": "assistant", 
            "content": f"已展示{'所有岗位' if not position else position}{f'在{city}' if city else ''}的岗位推荐数据"
        })

def query_skill_data(position=None):
    """
    查询本地Skill.csv数据集中的技能数据
    Args:
        position: 岗位名称
    Returns:
        DataFrame: 包含技能数据的DataFrame
    """
    try:
        df = pd.read_csv('Skill.csv')
        if position:
            df = df[df['positionName'].str.contains(position, case=False)]
        return df[['positionName', 'city', 'skill_list']]
    except Exception as e:
        st.error(f"读取技能数据失败: {e}")
        return None

def show_skill_recommendations(position=None):
    """
    展示技能推荐结果
    """
    df = query_skill_data(position)
    if df is None or df.empty:
        st.warning("没有找到匹配的技能数据")
        return
    
    # 显示数据表格
    st.dataframe(df)
    
    # 按城市分组显示技能数量
    fig, ax = plt.subplots(figsize=(10, 6))
    df.groupby('city')['skill_list'].count().plot.bar(ax=ax)
    ax.set_title('各城市技能需求数量')
    ax.set_ylabel('技能数量')
    st.pyplot(fig)

#查询职业技能
def handle_skill_query():
    # 获取用户输入的岗位
    position = st.session_state.skill_query.strip() or None
    
    # 添加用户查询记录到对话历史
    query_text = f"查询{position}岗位的技能要求"
    st.session_state.messages.append({"role": "user", "content": query_text})
    
    # 在右侧聊天框显示用户消息
    with st.chat_message("user"):
        st.markdown(query_text)
    
    # 在右侧聊天框显示助手响应
    with st.chat_message("assistant"):
        st.markdown(f"为您展示{position}岗位的技能要求：")
        
        # 1. 显示原始数据表格
        show_skill_recommendations(position)
        
        # 2. 调用LLM进行技能分析
        if len(key) > 1:  # 检查API Key是否有效
            with st.spinner("AI正在分析技能数据..."):
                # 准备分析提示词
                analysis_prompt = {
                    "role": "user",
                    "content": f"请分析以下{position}岗位的技能要求数据，总结关键技能点和地区差异:\n{query_skill_data(position).to_string()}"
                }
                
                # 调用GLM API进行分析
                analysis_response = ask_glm(
                    key, 
                    model, 
                    max_tokens, 
                    temperature, 
                    [analysis_prompt]
                )['choices'][0]['message']['content']
                
                # 显示分析结果
                st.subheader("AI技能分析")
                st.markdown(analysis_response)
        
def query_city_position_distribution(city=None):
    """
    查询城市岗位分布情况
    Args:
        city: 可选参数，指定城市名称
    Returns:
        DataFrame: 包含岗位分布数据的DataFrame
    """
    try:
        df = pd.read_csv('Skill.csv')
        if city:
            df = df[df['city'] == city]
        return df.groupby('positionName').size().reset_index(name='count')
    except Exception as e:
        st.error(f"读取数据失败: {e}")
        return None

def show_city_position_distribution(city=None):
    """
    展示城市岗位分布情况，包括饼图
    """
    df = query_city_position_distribution(city)
    if df is None or df.empty:
        st.warning("没有找到匹配的岗位数据")
        return
    
    # 设置中文字体
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
    plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
    
    # 显示数据表格
    st.dataframe(df)
    
    # 显示饼图
    fig, ax = plt.subplots(figsize=(10, 6))
    # 按岗位数量排序
    df = df.sort_values('count', ascending=False)
    # 只显示前5大岗位，其余合并为"其他"
    if len(df) > 5:
        other_count = df[5:]['count'].sum()
        df = df[:5]
        df = pd.concat([df, pd.DataFrame([{'positionName':'其他', 'count':other_count}])])
    
    df.plot.pie(y='count', labels=df['positionName'], autopct='%1.1f%%', ax=ax)
    ax.set_title(f"{city if city else '所有城市'}岗位分布情况")
    ax.set_ylabel('')
    st.pyplot(fig)

#查询岗位分布
def handle_city_position_query():
    # 获取用户输入的城市
    city = st.session_state.city_position_query.strip() or None
    
    # 添加用户查询记录到对话历史
    query_text = f"查询{city if city else '所有城市'}的岗位分布情况"
    st.session_state.messages.append({"role": "user", "content": query_text})
    
    # 在右侧聊天框显示用户消息
    with st.chat_message("user"):
        st.markdown(query_text)
    
    # 在右侧聊天框显示助手响应
    with st.chat_message("assistant"):
        st.markdown(f"已为您查询{'所有城市' if not city else city}的岗位分布情况：")
        # 调用分析函数显示表格和图表
        show_city_position_distribution(city)
        
        # 新增LLM分析部分
        if len(key) > 1:  # 检查API Key是否有效
            with st.spinner("AI正在分析岗位分布数据..."):
                # 准备分析提示词
                analysis_prompt = {
                    "role": "user",
                    "content": f"请分析{city if city else '全国'}的岗位分布情况，总结主要岗位类型和占比特点:\n{query_city_position_distribution(city).to_string()}"
                }
                
                # 调用GLM API进行分析
                analysis_response = ask_glm(
                    key, 
                    model, 
                    max_tokens, 
                    temperature, 
                    [analysis_prompt]
                )['choices'][0]['message']['content']
                
                # 显示分析结果
                st.subheader("AI岗位分布分析")
                st.markdown(analysis_response)
        
        # 添加文本响应到对话历史
        st.session_state.messages.append({
            "role": "assistant", 
            "content": f"已展示{'所有城市' if not city else city}的岗位分布情况（包含表格、图表和AI分析）"
        })

# 当用户输入了key之后，才可以显示对话信息。
if len(key) > 1:
    if prompt := st.chat_input():
        # 检测是否包含岗位分布关键词
        if "岗位分布" in prompt.lower():
            city = next((c for c in ["北京", "上海", "福州", "深圳", "广州"] if c in prompt), None)
            st.session_state.city_position_query = city if city else "所有城市"
            handle_city_position_query()
        # 检测是否包含岗位推荐关键词
        if "推荐" in prompt.lower() and any(pos in prompt.lower() for pos in ["java", "python", "前端", "数据", "测试"]):
            # 从输入中提取岗位关键词
            position = next((pos for pos in ["java", "python", "前端", "数据", "测试"] if pos in prompt.lower()), None)
            st.session_state.position_query = position
            handle_job_query()
        # 检测是否包含技能查询关键词
        elif "技能" in prompt.lower() and any(pos in prompt.lower() for pos in ["java", "python", "前端", "数据", "测试"]):
            position = next((pos for pos in ["java", "python", "前端", "数据", "测试"] if pos in prompt.lower()), None)
            st.session_state.skill_query = position
            handle_skill_query()
        else:
            # 将用户输入的文字（提示词）添加到message中。
            st.session_state.messages.append({"role": "user", "content": prompt})
            with st.chat_message("user"):
                st.markdown(prompt)

            with st.chat_message("assistant"):
                with st.spinner("请求中..."):
                    full_response = ask_glm(key, model, max_tokens, temperature, st.session_state.messages)['choices'][0]['message']['content']
                    st.markdown(full_response)
                    # 将LLM响应的内容也作为对话历史的一部分
                    message = {"role": "assistant", "content": full_response}
                    st.session_state.messages.append(message)

# 侧边栏部分
with st.sidebar:
    st.subheader('岗位推荐')
    # 岗位类型选择
    position_options = ["java", "python", "前端开发", "数据分析", "测试工程师"]
    position = st.selectbox('选择岗位类型', position_options, key='position_query')
    if position == "所有岗位":
        position = None
    
    # 城市选择
    city_options = [ "北京", "上海", "福州", "深圳", "广州"]
    city = st.selectbox('选择城市', city_options, key='city_job_query')
    if city == "所有城市":
        city = None
        
    st.button('查询岗位推荐', on_click=handle_job_query)
    
    # 新增职业技能查询部分
    st.subheader('职业技能查询')
    skill_position = st.selectbox('查询岗位技能', ["java", "python", "前端", "数据", "测试"], key='skill_query')
    st.button('查询职业技能', on_click=handle_skill_query)
    
    st.subheader('城市岗位分布查询')
    city_options = ["北京", "上海", "福州", "深圳", "广州"]
    city = st.selectbox('选择城市', city_options, key='city_position_query')
    if city == "所有城市":
        city = None
        
    st.button('查询岗位分布', on_click=handle_city_position_query)


#选择问题
import pandas as pd
import streamlit as st
from streamlit_echarts import st_echarts

# 侧边栏选择问题 - 修改为可选显示
show_analysis = st.sidebar.checkbox("显示数据分析选项", value=False)
if show_analysis:
    question = st.sidebar.radio(
        "选择问题",
        [
            "不同城市的数据挖掘算法工程师平均薪资分布",
            "不同融资阶段的公司规模分布",
            "各教育水平要求下不同工作年限的岗位数量",
            "不同薪资范围的算法工程师岗位数量分布",
            "不同技能标签的算法工程师岗位数量分布",
        ],
    )
else:
    question = None  # 添加默认值

# 在所有分析条件前添加question检查
if question and question == "不同城市的数据挖掘算法工程师平均薪资分布":
    # 读取数据
    file_path = '拉勾网2023招聘数据.csv'
    data = pd.read_csv(file_path)

    # 过滤数据
    filtered_data = data[(data['positionName'] == '算法工程师') & (data['keyword'] == '数据挖掘')]

    # 计算不同城市的平均薪资
    filtered_data['min_salary'] = filtered_data['salary'].str.extract('(\d+)k').astype(int)
    filtered_data['max_salary'] = filtered_data['salary'].str.extract('-(\d+)k').astype(int)
    filtered_data['avg_salary'] = (filtered_data['min_salary'] + filtered_data['max_salary']) / 2
    city_avg_salary = filtered_data.groupby('city')['avg_salary'].mean().reset_index()

    # 准备ECharts数据
    x_data = city_avg_salary['city'].tolist()
    y_data = city_avg_salary['avg_salary'].tolist()

    options = {
        "title": {"text": "不同城市的数据挖掘算法工程师平均薪资分布"},
        "tooltip": {"trigger": "axis"},
        "xAxis": {
            "type": "category",
            "data": x_data,
        },
        "yAxis": {"type": "value"},
        "series": [
            {
                "name": "平均薪资",
                "type": "bar",
                "data": y_data,
            },
        ],
    }

    st_echarts(options=options, height="400px")


# 2.不同融资阶段的公司规模分布
elif question == "不同融资阶段的公司规模分布":
    # 读取数据
    file_path = '拉勾网2023招聘数据.csv'
    data = pd.read_csv(file_path)

    # 过滤数据
    filtered_data = data[(data['positionName'] == '算法工程师') & (data['keyword'] == '数据挖掘')]

    # 按融资阶段分组统计公司规模
    finance_stage_size = filtered_data.groupby('financeStage')['companySize'].value_counts().unstack().fillna(0)

    # 准备ECharts数据
    x_data = finance_stage_size.index.tolist()
    series_data = []
    for size in finance_stage_size.columns:
        series_data.append({
            "name": size,
            "type": "bar",
            "stack": "总量",
            "data": finance_stage_size[size].tolist(),
        })

    options = {
        "title": {"text": "不同融资阶段的公司规模分布"},
        "tooltip": {"trigger": "axis"},
        "legend": {"data": finance_stage_size.columns.tolist()},
        "xAxis": {
            "type": "category",
            "data": x_data,
        },
        "yAxis": {"type": "value"},
        "series": series_data,
    }

    st_echarts(options=options, height="400px")


# 3.各教育水平要求下不同工作年限的岗位数量
elif question == "各教育水平要求下不同工作年限的岗位数量":
    # 读取数据
    file_path = '拉勾网2023招聘数据.csv'
    data = pd.read_csv(file_path)

    # 过滤数据
    filtered_data = data[(data['positionName'] == '算法工程师') & (data['keyword'] == '数据挖掘')]

    # 按教育水平和工作年限分组统计岗位数量
    education_workyear_count = filtered_data.groupby(['education', 'workYear']).size().unstack().fillna(0)

    # 准备ECharts数据
    x_data = education_workyear_count.index.tolist()
    series_data = []
    for workyear in education_workyear_count.columns:
        series_data.append({
            "name": workyear,
            "type": "line",
            "stack": "总量",
            "areaStyle": {},
            "emphasis": {"focus": "series"},
            "data": education_workyear_count[workyear].tolist(),
        })

    options = {
        "title": {"text": "各教育水平要求下不同工作年限的岗位数量"},
        "tooltip": {
            "trigger": "axis",
            "axisPointer": {"type": "cross", "label": {"backgroundColor": "#6a7985"}},
        },
        "legend": {"data": education_workyear_count.columns.tolist()},
        "toolbox": {"feature": {"saveAsImage": {}}},
        "grid": {"left": "3%", "right": "4%", "bottom": "3%", "containLabel": True},
        "xAxis": [
            {
                "type": "category",
                "boundaryGap": False,
                "data": x_data,
            }
        ],
        "yAxis": [{"type": "value"}],
        "series": series_data,
    }

    st_echarts(options=options, height="400px")


# 4.不同薪资范围的算法工程师岗位数量分布
elif question == "不同薪资范围的算法工程师岗位数量分布":
    # 读取数据
    file_path = '拉勾网2023招聘数据.csv'
    data = pd.read_csv(file_path)

    # 过滤数据
    filtered_data = data[(data['positionName'] == '算法工程师') & (data['keyword'] == '数据挖掘')]

    # 计算薪资范围
    filtered_data['min_salary'] = filtered_data['salary'].str.extract('(\d+)k').astype(int)
    filtered_data['max_salary'] = filtered_data['salary'].str.extract('-(\d+)k').astype(int)
    filtered_data['avg_salary'] = (filtered_data['min_salary'] + filtered_data['max_salary']) / 2

    # 定义薪资范围
    bins = [0, 10, 20, 30, 40, 50, float('inf')]
    labels = ['0-10k', '10-20k', '20-30k', '30-40k', '40-50k', '50k+']
    filtered_data['salary_range'] = pd.cut(filtered_data['avg_salary'], bins=bins, labels=labels)

    # 按薪资范围分组统计岗位数量
    salary_range_count = filtered_data['salary_range'].value_counts().reset_index()
    salary_range_count.columns = ['salary_range', 'count']

    # 准备ECharts数据
    pie_data = []
    for _, row in salary_range_count.iterrows():
        pie_data.append({
            "value": row['count'],
            "name": row['salary_range']
        })

    options = {
        "title": {"text": "不同薪资范围的算法工程师岗位数量分布", "left": "center"},
        "tooltip": {"trigger": "item"},
        "legend": {
            "orient": "vertical",
            "left": "left",
        },
        "series": [
            {
                "name": "薪资范围",
                "type": "pie",
                "radius": "50%",
                "data": pie_data,
                "emphasis": {
                    "itemStyle": {
                        "shadowBlur": 10,
                        "shadowOffsetX": 0,
                        "shadowColor": "rgba(0, 0, 0, 0.5)",
                    }
                },
            }
        ],
    }

    st_echarts(options=options, height="600px")


# 5.不同技能标签的算法工程师岗位数量分布
elif question == "不同技能标签的算法工程师岗位数量分布":
    # 读取数据
    file_path = '拉勾网2023招聘数据.csv'
    data = pd.read_csv(file_path)

    # 定义要统计的技能标签
    skills = ['Java', 'Python', 'C++', 'PHP', '深度学习', '人工智能']

    # 初始化统计字典
    skill_counts = {skill: 0 for skill in skills}

    # 遍历数据中的每一行，统计技能标签出现的次数
    for _, row in data.iterrows():
        row_text = ' '.join(map(str, row.values))
        for skill in skills:
            if skill in row_text:
                skill_counts[skill] += 1

    # 准备ECharts数据
    pie_data = []
    for skill, count in skill_counts.items():
        pie_data.append({
            "value": count,
            "name": skill
        })

    options = {
        "title": {"text": "不同技能标签的出现次数分布", "left": "center"},
        "tooltip": {"trigger": "item"},
        "legend": {
            "orient": "vertical",
            "left": "left",
        },
        "series": [
            {
                "name": "技能标签",
                "type": "pie",
                "radius": "50%",
                "data": pie_data,
                "emphasis": {
                    "itemStyle": {
                        "shadowBlur": 10,
                        "shadowOffsetX": 0,
                        "shadowColor": "rgba(0, 0, 0, 0.5)",
                    }
                },
            }
        ],
    }

    st_echarts(options=options, height="600px")
    

